/*  BlogBotService.java Version 104 - 18th May 2008
 *  BlogBotService.java is the main component that interprets a Blog Bots Needs
 *  See Versions for program version number
 *  Copyright (C) 2008  Andy Broomfield andy@newmediathinking.com
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/

package net.blogbotplatform.blogbot;

import java.util.ArrayList;

// import java.util.*;

public class BlogBotService {
	BlogBotPanel parent;
	
	boolean serviceWake;
	boolean firstStart = false;
	//set this if you want timed sleep
	/*boolean timedSleep = true;
	//set these to a sleep and wake hour
	int wakeHour = 7;
	int sleepHour = 20;*/
	
	//boolean useTwitter = false;
	
	TwitterBrigde tb;
	XMLBuffer myXMLBuffer;

	String filename;
	String filePath;
	String fileList ="";
	
	String arcPath;

	//sample and data array
	//IMPORTANT, Match the array size to account for WRITE_INTERVAL / 1000
	static final int SMALL_ARRAY_INDEX = 100;
	static final int MAX_ARRAY_INDEX = 3600;
	
	
	int[] sampleBuffer = new int[SMALL_ARRAY_INDEX];
	int sampleBufferIndex = 0;
	
	int[] dataValues = new int[MAX_ARRAY_INDEX];
	int dataValuesIndex = 0;
	
	int currentValue;

	int bufferStartTime;

	//sample timing variables
	long pulseTimer;
	int PULSE_INTERVAL = 50;
	
	long sampleTimer;
	int SAMPLE_INTERVAL = 2000;

	long writeTimer;
	int WRITE_INTERVAL = 3600000;
	
	long liveTimer;
	int LIVE_TIME_OUT = 18000;
	
	long currentTime;
	
	//new serial bridge
	BlogBotSerialBridge bbsb;
	
	//new simulator
	BlogBotSimulator bbsim;
	
	//new printer bridge
	LinePrinterBridge lp;
	
	//new archiver
	BlogBotArchive arc;
		
	//status modes
	int mode, previousMode;
	
	//status holding vars
	boolean liveMode = false;
	boolean timerInterupted = false;
	
	long liveStart;
	
	int randomNumber, lastRandom;
	
	//simulation flag
	boolean simulated = false;
	
	public BlogBotService(BlogBotPanel _parent) {
		parent = _parent;
		
		//when we begin, we havn't woken yet
		serviceWake = false;
	
		//set up a new twitter bridge
		tb = new TwitterBrigde();
		myXMLBuffer = new XMLBuffer();
		
		//set up the shop and loader
		new BlogBotShop();
		new BlogBotLoader();
	
		//set up the serial port
		System.out.println(fileList);
		
		//if the serial is null, open it!
		if(bbsb == null) {
			System.out.println("prepping serial port.");
			bbsb = new BlogBotSerialBridge();
			//bbsb.openSerialPort(portName, baudRate);
		}
		
		//set up dot matrix printer
		//manually change your portname! This should eventually be user configurable
		//mac = ?? linux = /dev/usb/lp0 windows = LPT1 or use your own!
		//maybe this could feed back to BlogBotPanel somehow with a dropdown list
		//String printerPort = "/dev/usblp0";
		//String printerPort = "LPT1";
		
		//if the printer is null, set it up!
		if(lp == null) {
			System.out.println("setting up dot matrix printer.");
			lp = new LinePrinterBridge();
		}
		
		//set the file path and start the xml buffer
		filePath = "logs/";
		myXMLBuffer.startXMLBuffer();

		//set up an archiver
		arcPath = "arc/";
		arc = new BlogBotArchive();
		
		//set default sample timers
		pulseTimer = System.currentTimeMillis() + PULSE_INTERVAL;
		sampleTimer = System.currentTimeMillis() + SAMPLE_INTERVAL;
		writeTimer = System.currentTimeMillis() + WRITE_INTERVAL;
		
		//System.out.println("Current Time " + System.currentTimeMillis());
		//System.out.println("Sample Time  " + sampleTimer);
	}

	void runBlogBotService() {
		//check if we are using timers, and check if we are in bounds ok.
		if (BlogBotShop.timedSleep == true) checkAlarmClock();
		//if we are simulated, best to run the simulator now
		if (simulated) { bbsim.runSimulator(); }
		//if there really is data in the data array
		if (BlogBotShop.lastByte == 255) {
			//set the currentTime as now, so it is consistent across samples
			currentTime = System.currentTimeMillis();
			//System.out.println("Current Time " + System.currentTimeMillis());
			//System.out.println("Sample Time  " + sampleTimer);
			
			//if this is the first time we have run, and we now have data, we can wake the service
			if (firstStart == true) {
				//wakeService();
				firstStart = false;
				//initilize the modes dataBuffer[0] will suffice
				mode = checkMode(BlogBotShop.dataBuffer[0]);
				previousMode = mode;
			}
			
			//check the pulse for data
			if (currentTime > pulseTimer) {
				System.out.print(".");
				//add what is in data buffer to sample buffer (means the very last data sample)
				sampleBuffer[sampleBufferIndex] = BlogBotShop.dataBuffer[0];
				//System.out.println("Current Sample " + sampleBuffer[sampleBufferIndex]);
				sampleBufferIndex++;
				if (sampleBufferIndex >= sampleBuffer.length) sampleBufferIndex = 0;
				//reset the pulseTimer
				pulseTimer = currentTime + PULSE_INTERVAL;
			}
			
			if (serviceWake == true) {
				if (currentTime > sampleTimer) {
					//System.out.print("*");
					//sample the sampleBuffer, and check if things have changed
					int sumOfSamples = 0;
					for (int i=0; i < sampleBufferIndex; i++) sumOfSamples += sampleBuffer[i];
					currentValue = sumOfSamples / (sampleBufferIndex);
					//now reset the sample Buffer
					sampleBufferIndex = 0;
					//once we have a current (averagised) value, add to the data array
					dataValues[dataValuesIndex] = currentValue; 
					dataValuesIndex++;
					if(dataValuesIndex >= dataValues.length) dataValuesIndex = 0;
					//add to the xml
					myXMLBuffer.fillXML();

					//now we need to check which mode (status) we're in, and if it has changed
					System.out.print("current value " + currentValue + " ");
					previousMode = mode;
					mode = checkMode(currentValue);
					System.out.println(mode);
					if (mode!=previousMode) {
						//we are different, so switch to live mode and blog now
						liveMode = true;
						//start or reset the live mode timer for time out.
						liveTimer = System.currentTimeMillis();
						//have we interupted the timer yet?
						if (timerInterupted == false) {
							//we should interupt it and blog right now!
							System.out.print("I");
							blogDataValues();
							timerInterupted = true;	
							liveStart = currentTime;
						}
						//we have some functions here to send live text to the printer (maybe using liveMode flags)
						blogCurrent();

					} else {
						//we are the same value, so time to get things back to normal.
						liveMode = false;
						//if we previously interupted the timer, we should reset it here (ammened, only after a live mode time out)
						if ((timerInterupted == true) && (System.currentTimeMillis() > liveTimer + LIVE_TIME_OUT)) {
							writeTimer = currentTime + WRITE_INTERVAL;
							timerInterupted = false;
							System.out.println(writeTimer);
							//add some lines here that send a printer line feed / spit out.
							lp.spitOut();
							arc.fillArc("\n");
							parent.outputTA.append("\n");
						}
					}


					//reset the sampleTimer
					sampleTimer = currentTime + SAMPLE_INTERVAL;
					//System.out.println("Sample Taken...");
				}

				//only trigger this if we have timed out without the timer being interupted
				if ((currentTime > writeTimer) && (timerInterupted == false)) {
					System.out.print("X");
					//we havn't done anything for a while, so lets blog about our average condition
					blogDataValues();
					lp.spitOut();
					arc.fillArc("\n");
					parent.outputTA.append("\n");
					writeTimer = currentTime + WRITE_INTERVAL;
				}
			} 
		}
	}
	
	void blogDataValues() {
		Utils.setTimeStamp();
		//would put understanding code here
		//so for instance, measure the light buffer average and comment on that!
		//find the total and average light exposure
		int averageData = 0;
		int totalData = 0;		
		
		String timeStamp = Utils.returnTimeStampAsText();
		System.out.println();
		System.out.println("TimeStamp: " + timeStamp);
		
		String blogText;
		String pBlogText;
		String interpText = "";
	
		for (int dataCount = 0;dataCount < dataValuesIndex; dataCount++) {
			totalData = totalData + dataValues[dataCount];
		}
		averageData = totalData / dataValuesIndex;
		
		int averageMode = checkMode(averageData);
		
		//check we have a valid mode, then fetch a different random comment from our last.
		if ((averageMode > -1) && (averageMode < BlogBotShop.NUM_OF_MODES)) {
			lastRandom = randomNumber;
			//System.out.println("avalible comments " + BlogBotShop.PAST_COMMENTS[averageMode].size());
			if (BlogBotShop.PAST_COMMENTS[averageMode].size() > 1) {
				while (lastRandom ==  randomNumber) {
					randomNumber = (int)Math.floor(Math.random() * BlogBotShop.PAST_COMMENTS[averageMode].size());
				}
			} else {
				randomNumber = 0;
			}
			System.out.println("Mode " + mode + " RandomNumber " + randomNumber);
			interpText = (String)BlogBotShop.PAST_COMMENTS[averageMode].get(randomNumber);
		} else {
			interpText = "I Don't Remember!";
		}
/*		if (mode != previousMode) {
			
			previousMode = mode;
		}*/
		
		
		blogText = timeStamp + " >> " + " Samples : " + (dataValuesIndex+1) + " Reading : " + totalData + " Average : " + averageData + " >> " + interpText;
		pBlogText = timeStamp + " >> " + " Samples : " + (dataValuesIndex+1) + " Reading : " + totalData + " Average : " + averageData + "\n         >> " + interpText;
		//BlogBotShop.screenBlogText = timeStamp + " >> " + " Samples : " + (dataValuesIndex+1) + " Exposure : " + totalData + " Average : " + averageData + "\n         >> " + interpText + "\n";
		parent.outputTA.append(pBlogText + "\n");
		arc.fillArc(pBlogText);
		
		System.out.println (blogText);
		
		//send to the printer
		lp.sendPrintln(pBlogText);
		
		//lets twitter how we have been
		if (BlogBotShop.useTwitter == true) {
			if(tb.checkAlive() == true) tb.update(blogText);
		}
		
		myXMLBuffer.writeXML(blogText, filePath);
		
		
		dataValuesIndex = 0;
	}
	
	void blogCurrent() {
		String liveText;
		String liveBlogLine;
		String pLiveBlogLine;
		int elapsedTime;
		String elapsedAsString;
		
		//gets how much time has elapsed since we went to live mode.
		elapsedTime = (int) (currentTime - liveStart) / 1000;
		
		//this fetch's live comments for blogging now.
		//first check that we have a valid mode,
		//then make sure we have different random comment from last time
		if ((mode > -1) && (mode < BlogBotShop.NUM_OF_MODES)) {
			lastRandom = randomNumber;
			if (BlogBotShop.LIVE_COMMENTS[mode].size() > 1) {
				while (lastRandom ==  randomNumber) {
					randomNumber = (int)Math.floor(Math.random() * BlogBotShop.LIVE_COMMENTS[mode].size());
				} 
			} else {
				randomNumber = 0;
			}
			System.out.println("Mode " + mode + " RandomNumber " + randomNumber);
			liveText = (String)BlogBotShop.LIVE_COMMENTS[mode].get(randomNumber);
		} else {
			liveText = "I'm feeling Dizzy!";
		}
		
		elapsedAsString = "+";
		if (elapsedTime < 100) elapsedAsString += "0";
		if (elapsedTime < 10) elapsedAsString += "0";
		elapsedAsString += elapsedTime;
		liveBlogLine = elapsedAsString + " >> " + currentValue + " >> " + liveText;
		pLiveBlogLine = elapsedAsString + "     >> " + liveText;
		//BlogBotShop.screenBlogText += elapsedAsString + "     >> " + liveText + "\n";
		parent.outputTA.append(pLiveBlogLine + "\n");
		arc.fillArc(liveBlogLine);
		
		System.out.println(liveBlogLine);
		
		//lets send this to the printer first!
		lp.sendPrintln(pLiveBlogLine);
		
		//optionally we can make a twitter update with the live text!
		if (BlogBotShop.useTwitter == true) {
			if(tb.checkAlive() == true) tb.update(liveBlogLine);
		}
	}
	
	//checks the mode by returning a mode value based upon serial data read in
	int checkMode(int sentValue) {
		int returnMode;
		if (sentValue < 30) {
			returnMode = 0; //led off
		} else if ((sentValue >= 40) && (sentValue < 50)) { 
			returnMode = 1; //led off
		} else if ((sentValue >= 60) && (sentValue < 90)) {
			returnMode = 2; //led green
		} else if ((sentValue >= 100) && (sentValue < 140)) {
			returnMode = 3; //led yellow
		} else if ((sentValue >= 150) && (sentValue < 170)) {
			returnMode = 4; //led red
		} else if ((sentValue >= 180) && (sentValue < 240)) {
			returnMode = 5; //led red
		} else if (((sentValue >= 30) && (sentValue < 40)) || ((sentValue >= 50) && (sentValue < 60)) || ((sentValue >= 90) && (sentValue < 100)) 
			|| ((sentValue >= 140) && (sentValue < 150)) || ((sentValue >= 170) && (sentValue < 180))) { 
			returnMode = mode; //keep to same mode as in now (an anti nuerontic feature) this introduces an interesting bug!
		} else {
			returnMode = -1;;
		}
		return returnMode;
	}
	
	void wakeService() {
		serviceWake = true;
		//load (or re-load) the configuration files
		BlogBotLoader.loadConfig();
		//if useTwitter is true, reset the twitter passwords
		if (BlogBotShop.useTwitter == true) tb.setTwitterAccount();
		//put some more code here, maybe send to twitter our wake status
		Utils.setTimeStamp();
		String timeStamp = Utils.returnTimeStampAsText();
		//output awake to textarea
		parent.outputTA.setText("");
		parent.outputTA.append(timeStamp + " >> Blog Bot Awake \n\n");
		arc.fillArc(timeStamp + " >> Blog Bot Awake\n\n");
		//print awake
		//I have no idea why I have to send it like this, but otherwise it just crahses!
		lp.sendPrint(timeStamp);
		lp.sendPrintln(" >> Blog Bot Awake");
		lp.spitOut();
		//twitter awake
		if (BlogBotShop.useTwitter == true) {
			if(tb.checkAlive() == true) tb.update(timeStamp + " >> Blog Bot Awake");
		}
	}
	
	void sleepService() {
		if (firstStart == false) {
			serviceWake = false;
			//put some more code here, maybe send to twitter our wake(sleep) status
			Utils.setTimeStamp();
			String timeStamp = Utils.returnTimeStampAsText();
			//output asleep to textarea
			parent.outputTA.append(timeStamp + " >> Blog Bot Asleep \n\n");
			arc.fillArc("\n" + timeStamp + " >> Blog Bot Asleep");
			arc.writeArc(arcPath);
			//print asleep
			lp.sendPrintln(timeStamp + " >> Blog Bot Asleep");
			lp.spitOut();
			//twitter asleep
			if (BlogBotShop.useTwitter) {
				if(tb.checkAlive() == true) tb.update(timeStamp + " >> Blog Bot Asleep");
			}
		}
	}

	//handles overnight, can be used as a simple sleep / wake timer
	//note : this take priorty over manual sleep wakes for now, needs to be changed.
	void checkAlarmClock() {
		Utils.setTimeStamp();
		int currentHour = Utils.hour;
		if ((currentHour >= BlogBotShop.wakeHour) && (currentHour < BlogBotShop.sleepHour) &&  (serviceWake == false)) wakeService();
		if (((currentHour >= BlogBotShop.sleepHour) || (currentHour < BlogBotShop.wakeHour)) && (serviceWake == true)) sleepService();
	}
	
	//recives changes in port settings, implements a change of port
	void changeSerialPort(String portName) {
		simulated = false;
		int baudRate = 9600;
		bbsb.closeSerialPort();
		if (portName.equals("Off")) return;
		if (portName.equals("Simulator")) {
			System.out.println("Starting the Simulator");
			if(bbsim == null) bbsim = new BlogBotSimulator();
			simulated = true;
		} else {
			bbsb.openSerialPort(portName, baudRate);
		}
	}
	
	void changeParallelPort(String portName) {
		lp.closeLinePrinterPort();
		lp.openLinePrinterPort(portName);
	}
	
	//implement a clean shut down that closes the ports
	void stopService() {
		sleepService();
		bbsb.closeSerialPort();
		lp.closeLinePrinterPort();
	}
}